package net.jmchilton.spring.collect.spec;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import net.jmchilton.spring.collect.spec.namespace.Namespace;
import net.jmchilton.spring.collect.spec.namespace.StaticFactory;

/**
 * 
 * @author John Chilton
 */
class Verifier {
  
  static void verify(final Namespace namespace) {
    for(final StaticFactory element : namespace.getStaticFactory()) {
      verifyStaticFactoryElement(element);
    }
  }

  public static String join(final Iterable<?> iterable) {
    final StringBuilder builder = new StringBuilder();
    for(Object object : iterable) {
      builder.append(builder.length() == 0 ? "" : ", ");
      builder.append(object.toString());
    }
    return builder.toString();
  }

  public static void fail(final String methodName, final Class<?> returnType, final Iterable<Class<?>> inputs) {
    final StringBuilder failStr = new StringBuilder("Failed to find method with name " + methodName);
    if(returnType != null) {
      failStr.append(" and return type " + returnType);
    }
    if(inputs != null) {
      failStr.append(" with input signature (" + join(inputs) + ")");
    }
    throw new RuntimeException(failStr.toString());
  }
  
  
  
  static void verifyStaticFactoryElement(final StaticFactory element) {
    final Class<?> clazz = ReflectionUtils.getClass(element.getClazz());
    final ParameterPermuations inputPermutations = new ParameterPermuations(element.getParameter());
    final List<Method> methods = new ArrayList<Method>(Arrays.asList(clazz.getMethods()));

    final String methodName = element.getMethod();
    for(final Method method : new ArrayList<Method>(methods)) {
      if(!method.getName().equals(methodName)) {
        methods.remove(method);
      }      
    }    
    if(methods.size() == 0) {
      fail(methodName, null, null);
    } 
    
    final Class<?> returnClass = ReflectionUtils.getClass(element.getType());
    for(final Method method : new ArrayList<Method>(methods)) {
      if(!method.getReturnType().equals(returnClass)) {
        methods.remove(method);
      }
    }
    if(methods.size() == 0) {
      fail(methodName, returnClass, null);
    }
    
    for(final List<Class<?>> inputPermutation : inputPermutations) {
      boolean inputFound = false;
      for(final Method method : methods) {
        if(Arrays.equals(method.getParameterTypes(), inputPermutation.toArray(new Class[inputPermutation.size()]))) {
          inputFound = true;          
        }
      }
      if(!inputFound) {
        fail(methodName, returnClass, inputPermutation);        
      }
    }
  } 
}



